package top.zhmq.demo.config;

import com.sun.org.apache.bcel.internal.generic.NEW;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import top.zhmq.demo.service.UserDetailsServiceImpl;

import javax.sql.DataSource;

/**
 * MVC Security管理配置的自定义WebSecurityConfigurerAdapter类
 *
 * @author Glory
 * @classname SecurityConfig
 * @create 2021-04-06 12:21
 */

@EnableWebSecurity
/*
@EnableWebSecurity等同于
    @EnableGlobalAuthentication
    @Import(WebSecurityConfiguration.class)
    @Configuration同时使用
@EnableGlobalAuthentication:开启自定义的全局认证
@Import(WebSecurityConfiguration.class):进行自动化配置
@Configuration:将当前类作为配置类
 */
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
/*
在控制器中对应的请求中添加Secured注解实现，此时需要在WebSecurityConfig中添加@EnableGlobalMethodSecurity(securedEnabled = true)
prePostEnabled 实现注解添加权限认证控制,需要在WebSecurityConfig中添加@EnableGlobalMethodSecurity(prePostEnabled = true)
 */
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    //用户授权管理自定义配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 自定义用户授权管理
        http.authorizeRequests()
                .antMatchers("/").permitAll()//访问路径为/的直接放行
                .antMatchers("/userRegister").permitAll()//注册界面放行
                .antMatchers("/login/**").permitAll()// 需要对static文件夹下静态资源进行统一放行
                //有common或者vip角色的才允许访问
                .antMatchers("/errorpage").permitAll()
                .antMatchers("/test").denyAll()
//                .antMatchers("/testip").access("hasIpAddress('**') or hasIpAddress('125.47.41.148')")//只有指定ip能访问
                .antMatchers("/detail/common/**").hasAnyAuthority("ROLE_vip", "ROLE_common")/* hasAnyRole("vip","common")*/
                .antMatchers("/detail/vip/**").hasRole("vip")//只有有vip角色的才允许访问
                .anyRequest().authenticated();//其余请求会要求用户进行登录认证

//        http.exceptionHandling().
//                accessDeniedHandler(myAccessDeniedHandler);//自定义拒绝访问处理

        // 自定义用户登录控制
        http.formLogin()
                .loginPage("/userLogin").permitAll()
                .usernameParameter("name").passwordParameter("pwd")
                .defaultSuccessUrl("/")
                .failureUrl("/userLogin?error");

        // 自定义用户退出控制
        http.logout()
                .logoutUrl("/mylogout")
                .logoutSuccessUrl("/");

        // 定制Remember-me记住我功能
        http.rememberMe()
                .rememberMeParameter("rememberme")
                .tokenValiditySeconds(200)
                // 对cookie信息进行持久化管理
                .tokenRepository(tokenRepository());

        //可以关闭Spring Security默认开启的CSRF防护功能
//        http.csrf().disable();

    }

    // 持久化Token存储
    @Bean
    public JdbcTokenRepositoryImpl tokenRepository() {
        JdbcTokenRepositoryImpl jr = new JdbcTokenRepositoryImpl();
        jr.setDataSource(dataSource);
        return jr;
    }

    //SpringSecurity自定义密码加密


    //用户身份认证自定义配置
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        //  密码需要设置编码器
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        MyPasswordEncoder encoder1 = new MyPasswordEncoder();
//        // 1、使用内存用户信息，作为测试使用
//        auth.inMemoryAuthentication().passwordEncoder(encoder)
//                .withUser("shitou").password(encoder.encode("123456")).roles("common")
//                .and()
//                .withUser("李四").password(encoder.encode("123456")).roles("vip");
        /*
        自定义用户认证，必须设置密码编码器保护，推荐BCryptPasswordEncoder
        自定义用户认证时，可以自定义用户角色roles，也可以自定义用户权限authorities
        在自定义时，可以为同一个用户指定多个角色或者权限
         */
        // 2、使用JDBC进行身份认证
//        String userSQL = "select username,password,valid from t_customer " +
//                "where username = ?";
//        String authoritySQL = "select c.username,a.authority from t_customer c,t_authority a," +
//                "t_customer_authority ca where ca.customer_id=c.id " +
//                "and ca.authority_id=a.id and c.username =?";
//        auth.jdbcAuthentication().passwordEncoder(encoder)
//                .dataSource(dataSource)
//                .usersByUsernameQuery(userSQL)
//                .authoritiesByUsernameQuery(authoritySQL);
        /*
        自定义用户查询的SQL语句时，必须返回username、password、valid三个字段信息
        自定义其权限查询SQL语句时，必须返回username、authority两个字段信息
        否则会报异常
         */

//         3、使用UserDetailsService进行身份认证
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
        //auth.userDetailsService(userDetailsService).passwordEncoder(encoder1);
    }
}
